﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;

namespace IA.Shopping.UI
{
    public class Agente
    {
        #region Atributos

        public bool _ativo = true;
        private Int32 _iD;
        private Int32 _sentidoCaminhadaX = 0;
        private Int32 _sentidoCaminhadaY = 0;
        private Point _posicaoInicial;
        private Point _posicaoAtual;
        private Double _dinheiro;
        private Stack<Bloco> _caminhosPercorricos = null;
        private const double CUSTO_MOVIMENTO = 2.5;
        private bool _esperandoDecisaoDeCompra = false;

        #endregion

        #region Propriedades
        public Int32 ID
        {
            get { return _iD; }
            set { _iD = value; }
        }
        public Double Dinheiro
        {
            get { return _dinheiro; }
            set { _dinheiro = value; }
        }
        #endregion

        #region Eventos
        public event EventHandler<AgenteEventArgs> PositionChanged;
        public event EventHandler<Boolean> BuscaCompleted;

        #endregion

        public Agente(Point posicaoInicial, int id, Double dinheiro)
        {
            this._dinheiro = dinheiro;
            this._posicaoInicial = new Point { X = posicaoInicial.X, Y = posicaoInicial.Y };
            this._posicaoAtual = posicaoInicial;
            this._iD = id;
            this._caminhosPercorricos = new Stack<Bloco>();
            Shopping.AgentesBuscaCompleta = new List<Agente>();
            Console.WriteLine(" ");
        }

        public void BuscaEmProfundidade(Object state)
        {
            System.Threading.Thread.Sleep(TimeSpan.FromSeconds(Util.TEMPO));
            var blocoAtual = Shopping.Matriz[(int)this._posicaoAtual.X, (int)this._posicaoAtual.Y];

            //Caso algum agente tenha parado e um outro passar do lado dele
            //o agente parado vai passar seu dinheiro restante para o outro. 
            Agente agente;
            if ((agente = olharAoRedor()) != null)
            {
                Console.Write(String.Format("\nAgente{0} passou {1} para Agente{2}", this._iD, agente._dinheiro, agente._iD));
                this._dinheiro += agente._dinheiro;
                agente = null;
            }

            if (!PodeAndar())
            {
                this.Completed();
                return;
            }

            if (blocoAtual.possuiLoja())
            {
                _esperandoDecisaoDeCompra = Shopping.avisarSobreLojaEncontrada(this, blocoAtual.Loja);
                while (_esperandoDecisaoDeCompra)
                {
                    if (Shopping.prontoParaCompra())
                    {
                        blocoAtual.Loja.ComprarItens(this, Shopping.ItensAComprar);
                        _esperandoDecisaoDeCompra = false;
                        Shopping.RegistroLoja.Clear();
                    }
                }
            }

            if (this.AndarEsquerda(blocoAtual))
            {
                this.BuscaEmProfundidade(state);
            }
            else if (this.AndarDireita(blocoAtual))
            {
                this.BuscaEmProfundidade(state);
            }
            else
            {

                if (_posicaoAtual.X == _posicaoInicial.X && _posicaoAtual.Y == _posicaoInicial.Y)
                {
                    this.VisitaBloco(blocoAtual, Cor.PRETO);
                    Console.Write(String.Format("\nAgente{0} Done!", this._iD));
                    this.Completed();
                    return;
                }
                this.VisitaBloco(blocoAtual, Cor.PRETO);
                this.AtualizarUI(Shopping.Matriz[(int)this._posicaoAtual.X, (int)this._posicaoAtual.Y]);
                this.BuscaEmProfundidade(state);
            }
        }

        private void Completed()
        {
            _ativo = false;

            if (this.BuscaCompleted != null)
            {
                Shopping.AgentesBuscaCompleta.Add(this);

                if (Shopping.AgentesBuscaCompleta.Count == Shopping.QTDAgentes)
                    this.BuscaCompleted.Invoke(this, true);

            }

        }

        private void AtualizarUI(Bloco blocoAtual)
        {
            if (this.PositionChanged != null)
            {
                this.PositionChanged.Invoke(this, new AgenteEventArgs
                {
                    AgenteID = this._iD,
                    Posicao = blocoAtual.Posicao,
                    BlocoCor = blocoAtual.Cor,
                    Dinheiro = this.Dinheiro
                });
            }
        }

        private bool AndarEsquerda(Bloco blocoAtual)
        {
            var proximoX = this._posicaoAtual.X;

            if (this._sentidoCaminhadaX == 0)
                this._sentidoCaminhadaX = this.VerificarSentidoCaminhadaX();

            proximoX += this._sentidoCaminhadaX;

            if (proximoX > Util.DIMENSAO_MATRIZ - 1 || proximoX < 0)
                return false;
            {
                var proximoBloco = Shopping.Matriz[(int)proximoX, (int)this._posicaoAtual.Y];

                if (proximoBloco.Cor.Equals(Cor.BRANCO))
                {

                    proximoBloco.Cor = Cor.CINZA;
                    VisitaBloco(blocoAtual, Cor.CINZA);
                    VisitaBloco(proximoBloco, Cor.CINZA);
                    return true;
                }
                else if (proximoBloco.Cor.Equals(Cor.CINZA))
                {
                    return false;
                }
                else
                    return false;
            }
        }

        private bool AndarEsquerdaBackup(Bloco blocoAtual)
        {
            var proximoX = this._posicaoAtual.X;
            var proximoY = this._posicaoAtual.Y;

            if (this._posicaoInicial.X == 0 &&
             this._posicaoInicial.Y == 0)
            {
                proximoX += 1;
            }
            else if (this._posicaoInicial.X == 0 &&
                this._posicaoInicial.Y == Util.DIMENSAO_MATRIZ - 1)
            {
                proximoY -= 1;
            }
            else if (this._posicaoInicial.X == Util.DIMENSAO_MATRIZ - 1 &&
                this._posicaoInicial.Y == 0)
            {
                proximoY += 1;
            }
            else
            {
                proximoX -= 1;
            }


            if (proximoX > Util.DIMENSAO_MATRIZ - 1 || proximoX < 0)
                return false;
            {
                var proximoBloco = Shopping.Matriz[(int)proximoX, (int)proximoY];

                if (proximoBloco.Cor.Equals(Cor.BRANCO))
                {
                    VisitaBloco(blocoAtual, Cor.CINZA);
                    VisitaBloco(proximoBloco, Cor.CINZA);
                    return true;
                }
                else if (proximoBloco.Cor.Equals(Cor.CINZA))
                {
                    return false;
                }
                else
                    return false;
            }
        }

        private bool AndarDireitaBackup(Bloco blocoAtual)
        {
            var proximoX = this._posicaoAtual.X;
            var proximoY = this._posicaoAtual.Y;

            if (this._posicaoInicial.X == 0 &&
            this._posicaoInicial.Y == 0)
            {
                proximoY += 1;
            }
            else if (this._posicaoInicial.X == 0 &&
                this._posicaoInicial.Y == Util.DIMENSAO_MATRIZ - 1)
            {
                proximoX += 1;
            }
            else if (this._posicaoInicial.X == Util.DIMENSAO_MATRIZ - 1 &&
                this._posicaoInicial.Y == 0)
            {
                proximoX -= 1;
            }
            else
            {
                proximoY -= 1;
            }

            if (proximoY > Util.DIMENSAO_MATRIZ - 1 || proximoY < 0)
                return false;
            else
            {
                var proximoBloco = Shopping.Matriz[(int)proximoX, (int)proximoY];

                if (proximoBloco.Cor.Equals(Cor.BRANCO))
                {

                    Console.Write(String.Format("\nAgente{2} Bloco: {0}{1} Branco ", proximoBloco.Posicao.X, proximoBloco.Posicao.Y, this._iD));
                    VisitaBloco(blocoAtual, Cor.CINZA);
                    VisitaBloco(proximoBloco, Cor.CINZA);
                    return true;
                }
                else if (proximoBloco.Cor.Equals(Cor.CINZA))
                {
                    return false;
                }
                else
                    return false;
            }
        }

        private bool AndarDireita(Bloco blocoAtual)
        {
            var proximoY = this._posicaoAtual.Y;

            if (this._sentidoCaminhadaY == 0)
                this._sentidoCaminhadaY = this.VerificarSentidoCaminhadaY();

            proximoY += this._sentidoCaminhadaY;

            if (proximoY > Util.DIMENSAO_MATRIZ - 1 || proximoY < 0)
                return false;
            else
            {
                var proximoBloco = Shopping.Matriz[(int)this._posicaoAtual.X, (int)proximoY];

                if (proximoBloco.Cor.Equals(Cor.BRANCO))
                {
                    proximoBloco.Cor = Cor.CINZA;
                    Console.Write(String.Format("\nAgente{2} Bloco: {0}{1} Branco ", proximoBloco.Posicao.X, proximoBloco.Posicao.Y, this._iD));
                    VisitaBloco(blocoAtual, Cor.CINZA);
                    VisitaBloco(proximoBloco, Cor.CINZA);
                    return true;
                }
                else if (proximoBloco.Cor.Equals(Cor.CINZA))
                {
                    return false;
                }
                else
                    return false;
            }
        }

        private void VisitaBloco(Bloco bloco, Cor cor)
        {
            bloco.Cor = cor;
            if (bloco.Cor == Cor.CINZA)
            {
                if (this._caminhosPercorricos.Count() == 0 || this._caminhosPercorricos.ElementAt(0) != bloco)
                {
                    this._posicaoAtual = bloco.Posicao;
                    bloco.AgenteID = this._iD;
                    this._caminhosPercorricos.Push(bloco);
                    Console.Write(String.Format("\nAgente{2} Push: {0}{1} ", bloco.Posicao.X, bloco.Posicao.Y, this._iD));
                }
            }
            else
            {
                if (this._caminhosPercorricos.Count > 0)
                {
                    var blocoDestino = this._caminhosPercorricos.Pop();
                    this._posicaoAtual = blocoDestino.Posicao;
                    Console.Write(String.Format("\nAgente{2} Pop: {0}{1} ", blocoDestino.Posicao.X, blocoDestino.Posicao.Y, this._iD));
                }
            }
            this.AtualizarUI(bloco);
        }

        private Int32 VerificarSentidoCaminhadaX()
        {
            if (this._posicaoInicial.X == 0 &&
                this._posicaoInicial.Y == 0)
            {
                return 1;
            }
            else if (this._posicaoInicial.X == 0 &&
                this._posicaoInicial.Y == Util.DIMENSAO_MATRIZ - 1)
            {
                return 1;
            }

            else if (this._posicaoInicial.X == Util.DIMENSAO_MATRIZ - 1 &&
                this._posicaoInicial.Y == 0)
            {
                return -1;
            }
            else
            {
                return -1;
            }
        }

        private Int32 VerificarSentidoCaminhadaY()
        {
            if (this._posicaoInicial.X == 0 &&
              this._posicaoInicial.Y == 0)
            {
                return 1;
            }
            else if (this._posicaoInicial.X == 0 &&
                this._posicaoInicial.Y == Util.DIMENSAO_MATRIZ - 1)
            {
                return -1;
            }
            else if (this._posicaoInicial.X == Util.DIMENSAO_MATRIZ - 1 &&
                this._posicaoInicial.Y == 0)
            {
                return 1;
            }
            else
            {
                return -1;
            }
        }

        private bool PodeAndar()
        {
            return ((this._dinheiro -= CUSTO_MOVIMENTO) >= 0 &&
                Shopping.FaltaComprarAlgumItem());
        }

        public Agente olharAoRedor()
        {
            foreach (Agente agente in Shopping.Agentes)
            {

                if (!agente._ativo && agente._dinheiro > 0)
                {
                    //Cima
                    if (agente._posicaoAtual.X == this._posicaoAtual.X - 1 &&
                        agente._posicaoAtual.Y == this._posicaoAtual.Y)
                    {
                        return agente;
                    }
                    //Baixo 
                    else if (agente._posicaoAtual.X == this._posicaoAtual.X + 1 &&
                        agente._posicaoAtual.Y == this._posicaoAtual.Y)
                    {
                        return agente;
                    }
                    //Direita
                    else if (agente._posicaoAtual.X == this._posicaoAtual.X &&
                        agente._posicaoAtual.Y == this._posicaoAtual.Y + 1)
                    {
                        return agente;
                    }
                    //Esquerda
                    else if (agente._posicaoAtual.X == this._posicaoAtual.X &&
                       agente._posicaoAtual.Y == this._posicaoAtual.Y - 1)
                    {
                        return agente;
                    }
                }
            }
            return null;
        }
    }
}
